package com.thinvent.smart.chain.common;

import com.alipay.mychain.sdk.api.MychainClient;
import com.alipay.mychain.sdk.api.utils.Utils;
import com.alipay.mychain.sdk.common.VMTypeEnum;
import com.alipay.mychain.sdk.crypto.keypair.Keypair;
import com.alipay.mychain.sdk.domain.account.Identity;
import com.alipay.mychain.sdk.errorcode.ErrorCode;
import com.alipay.mychain.sdk.message.transaction.contract.*;
import com.alipay.mychain.sdk.utils.ByteUtils;
import com.alipay.mychain.sdk.vm.EVMOutput;
import com.alipay.mychain.sdk.vm.EVMParameter;

import java.math.BigInteger;

public class CallChain {
    private static MychainClient sdk;
    private static Keypair userKeypair;
    public String testContractId;
    public byte[] contractCode;
    private static Identity userIdentity;

    public CallChain(String account, String userPassword) {
        ChainSdk chainSdk = new ChainSdk();
        chainSdk.init(account, userPassword);
        sdk = chainSdk.getSdk(account, userPassword);
        userKeypair = chainSdk.getUserKeypair(account, userPassword);
        userIdentity = chainSdk.getUserIdentity(account, userPassword);
    }

    /**
     * 设置合约id
     *
     * @param testContractId
     */
    public CallChain testContractId(String testContractId) {
        this.testContractId = testContractId;
        return this;
    }

    /**
     * 设置合约字节
     *
     * @param contractCode
     */
    public CallChain contractCode(String contractCode) {
        this.contractCode = ByteUtils.hexStringToBytes(contractCode);
        return this;
    }

    /**
     * 设置合约字节
     *
     * @param contractCode
     */
    public void contractCode(byte[] contractCode) {
        this.contractCode = contractCode;
    }

    /**
     * 调用合约
     *
     * @param parameters
     * @return
     */
    public EVMOutput callContract(EVMParameter parameters) {
        CallContractRequest request = new CallContractRequest(userIdentity
                , Utils.getIdentityByName(testContractId)
                , parameters
                , BigInteger.ZERO
                , VMTypeEnum.EVM);
        CallContractResponse callContractResponse = sdk.getContractService().callContract(request);
        if (callContractResponse.getTransactionReceipt().getResult() != 0) {
            throw new RuntimeException(getErrorMsg((int) callContractResponse
                    .getTransactionReceipt()
                    .getResult()));
        } else {
            byte[] output = callContractResponse.getTransactionReceipt().getOutput();
            System.out.println("-----------" + new String(output));
            EVMOutput contractReturnValues = new EVMOutput(ByteUtils.toHexString(output));
            return contractReturnValues;
        }
    }

    /**
     * 部署合约
     *
     * @param parameters
     * @return
     */
    public boolean deployContract(EVMParameter parameters) {
        DeployContractRequest request = new DeployContractRequest(userIdentity,
                Utils.getIdentityByName(testContractId), contractCode, VMTypeEnum.EVM,
                parameters, BigInteger.ZERO);

        DeployContractResponse deployContractResult = sdk.getContractService().deployContract(request);
        // deploy contract
        if (!deployContractResult.isSuccess()
                || deployContractResult.getTransactionReceipt().getResult() != 0) {
            throw new RuntimeException(getErrorMsg((int) deployContractResult
                    .getTransactionReceipt()
                    .getResult()));
        } else {
            return true;
        }
    }

    public boolean updateContract() {
        UpdateContractRequest request = new UpdateContractRequest(
                Utils.getIdentityByName(testContractId)
                , contractCode
                , VMTypeEnum.EVM);
        UpdateContractResponse updateContractResult;
        updateContractResult = sdk.getContractService().updateContract(request);

        // deploy contract
        if (!updateContractResult.isSuccess()
                || updateContractResult.getTransactionReceipt().getResult() != 0) {
            throw new RuntimeException(getErrorMsg((int) updateContractResult
                    .getTransactionReceipt()
                    .getResult()));
        } else {
            return true;
//            logger.info("update contract success.");
        }
    }

    private static String getErrorMsg(int errorCode) {
        int minMychainSdkErrorCode = ErrorCode.SDK_INTERNAL_ERROR.getErrorCode();
        if (errorCode < minMychainSdkErrorCode) {
            return ErrorCode.valueOf(errorCode).getErrorDesc();
        } else {
            return ErrorCode.valueOf(errorCode).getErrorDesc();
        }
    }
}
